package zdream.control.test.world.sink;

import org.junit.Test;

import zdream.control.foe.mm.MegaMan;
import zdream.control.input.SimpleWorldInput;
import zdream.control.input.WorldInput;
import zdream.control.level.LevelManagerForTest;
import zdream.control.profile.mm.MegaManProfile;
import zdream.control.region.Region;
import zdream.control.region.SimpleRegionBuilder;
import zdream.control.region.TerrainConstant;
import zdream.control.world.LevelWorld;
import zdream.control.world.MFoeConstants;
import zdream.control.world.buff.SinkInWaterBuff;

import static org.junit.Assert.*;

public class SinkInWaterCase {
	@Test
	public void testWaterRegion() {
		Region region = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("  ~~~~~~~~ ~~~           ")
						.terrain("~~                       ")
						.terrain("                         ")
						.terrain("     -+                  ")
						.terrain(" #    +    ==            ")
						.terrain("      +#                 ")
						.terrain("           ~~            ")
						.terrain("                         ")
						.terrain("           ==            ")
						.terrain("  =                      ")
						.terrain("   == =====              ")
						.terrain("            =            ")
						.terrain("   -                     ")
						.terrain("                         ")
						.build()
				.build();
		System.out.println(region);
		assertEquals(1, region.rooms.length);

		final int width = region.rooms[0].width;
		final int height = region.rooms[0].height;
		final byte[][] terrains = region.rooms[0].terrains;
		final byte[][] fillers = region.rooms[0].fillers;

		Region checker = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("      +                  ")
						.terrain(" #    +                  ")
						.terrain("      +#                 ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.terrain("                         ")
						.build()
				.build();
		assertEquals(checker.rooms[0].width, width);
		assertEquals(checker.rooms[0].height, height);

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				assertEquals("(" + x + "," + y + ")", checker.rooms[0].terrains[x][y], terrains[x][y]);
			}
		}

		Region checker2 = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("  ############           ")
						.terrain("##############           ")
						.terrain("##############           ")
						.terrain("##############           ")
						.terrain("# ### ########           ")
						.terrain("# ### # ###  #           ")
						.terrain("# ### ########           ")
						.terrain("# ### ########           ")
						.terrain("# ### ########           ")
						.terrain("# ### ##### ##           ")
						.terrain("#  ## ##### ##           ")
						.terrain("#           ##           ")
						.terrain("#  #         #           ")
						.terrain("#            #           ")
						.build()
				.build();
		assertEquals(checker2.rooms[0].width, width);
		assertEquals(checker2.rooms[0].height, height);

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				if (checker2.rooms[0].terrains[x][y] == TerrainConstant.TERRAIN_SOLID) {
					assertEquals("(" + x + "," + y + ")", TerrainConstant.FILLER_WATER, fillers[x][y]);
				} else {
					assertEquals("(" + x + "," + y + ")", TerrainConstant.FILLER_EMPTY, fillers[x][y]);
				}
			}
		}

	}

	@Test
	public void testJumpInWater() {
		Region region = new SimpleRegionBuilder(this.getClass().getSimpleName())
				.withRoomPositions("1")
				.withRoom(1)
						.terrain("##~~~~~~~~~~~~~~~~~~~~~##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##                     ##")
						.terrain("##         S           ##")
						.terrain("#########################")
						.terrain("#########################")
						.build()
				.withStartPoint('S')
				.build();
		System.out.println(region);
		assertEquals(1, region.rooms.length);

		final byte[][] terrains = region.rooms[0].terrains;
		final byte[][] fillers = region.rooms[0].fillers;
		final int width = region.rooms[0].width;
		final int height = region.rooms[0].height;

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				if (x < 2 || y < 2 || x >= width - 2) {
					assertEquals("(" + x + "," + y + ")", TerrainConstant.TERRAIN_SOLID, terrains[x][y]);
					assertEquals("(" + x + "," + y + ")", TerrainConstant.FILLER_EMPTY, fillers[x][y]);
				} else {
					assertEquals("(" + x + "," + y + ")", TerrainConstant.TERRAIN_EMPTY, terrains[x][y]);
					assertEquals("(" + x + "," + y + ")", TerrainConstant.FILLER_WATER, fillers[x][y]);
				}
			}
		}

		MegaManProfile p1 = new MegaManProfile();

		LevelManagerForTest manager = LevelManagerForTest.builder()
				.withPlayer1(p1)
				.build();
		assertEquals(LevelWorld.STATE_NONE, manager.state);
		manager.applyWorld(region);
		manager.runUntil(ignored -> manager.state == LevelWorld.STATE_PLAYING);
		assertNotNull(manager.player1);

		MegaMan player1 = (MegaMan) manager.player1;
		System.out.println("Test 1: 就算进水里, 第一帧是没有 SinkInWater 的 buff 的. 这个可以改也可以不改");
		assertFalse(player1.hasBuff(SinkInWaterBuff.CATEGORY));

		System.out.println("Test 2: 过一帧之后一定有 SinkInWater 的 buff");
		manager.runOneFrame();
		assertTrue(player1.hasBuff(SinkInWaterBuff.CATEGORY));
		assertTrue(player1.findOneBuff(SinkInWaterBuff.CATEGORY).is(MFoeConstants.CATEGORY_GRAVITY_ASSOCIATED));
		assertEquals(SinkInWaterBuff.GRAVITY_SCALE_IN_WATER, player1.gravityScale, 0);
		assertTrue(player1.gravityDown);

		SimpleWorldInput input = new SimpleWorldInput();
		p1.setInput(input);
		assertSame(input, player1.controlModule.input);

		System.out.println("Test 3: 在水中跳跃, 最大高度 > 9");
		final float startY = player1.anchorY;
		float[] maxY = new float[] { startY };
		input.setDown(WorldInput.MAP_JUMP);
		long startTime = player1.world.ticker.activeTime;

		manager.everyTick(ignored -> {
			assertTrue("你应该在水里, 不应该在车底", player1.hasBuff(SinkInWaterBuff.CATEGORY));
			System.out.printf("时间 %d: y = %4.2f, inAir = %s%n",
					player1.world.ticker.activeTime, player1.anchorY, player1.inAir);
			player1.flush();
			if (maxY[0] < player1.anchorY) {
				maxY[0] = player1.anchorY;
			}
		});
		manager.runUntil(ignored -> !player1.inAir && maxY[0] > startY);
		assertTrue(player1.world.ticker.activeTime > startTime + 1000);

		System.out.printf("经过了 %d 毫秒, 跳回到了地面上. 水中跳跃最高高度是 %4.2f%n",
				player1.world.ticker.activeTime - startTime, maxY[0] - startY);

		assertTrue(maxY[0] - startY > 9);
		assertTrue(maxY[0] + player1.boxHeight - startY < 11);
	}
}
